ggplot2ggplot2 functions and elements of the layersggplot2library(tidyverse)
library(knitr)
library(broom)
library(stringr)
library(lubridate)
library(ggmap)
library(ggrepel)
library(gridExtra)
library(pander)
options(digits = 3)
set.seed(1234)Google defines a grammar as “the whole system and structure of a language or of languages in general, usually taken as consisting of syntax and morphology (including inflections) and sometimes also phonology and semantics”.1 Others consider a grammar to be “the fundamental principles or rules of an art or science”.2 Applied to visualizations, a grammar of graphics is a grammar used to describe and create a wide range of statistical graphics.3
The layered grammar of graphics approach is implemented in ggplot2, a widely used graphics library for R. All graphics in this library are built using a layered approach, building layers up to create the final graphic.
Layers are used to create the objects on a plot. They are defined by five basic parts:
Layers are typically related to one another and share many common features. For instance, multiple layers can be built using the same underlying data. An example would be a scattterplot overlayed with a smoothed regression line to summarize the relationship between the variables:
Data defines the source of the information to be visualized, but is independent from the other elements. So a layered graphic can be built which can utilize different data sources while keeping the other components the same. Here is a portion of a dataset contained in the ggplot2 package: mpg.
head(mpg) %>%
kable(caption = "Dataset of automobiles")| manufacturer | model | displ | year | cyl | trans | drv | cty | hwy | fl | class |
|---|---|---|---|---|---|---|---|---|---|---|
| audi | a4 | 1.8 | 1999 | 4 | auto(l5) | f | 18 | 29 | p | compact |
| audi | a4 | 1.8 | 1999 | 4 | manual(m5) | f | 21 | 29 | p | compact |
| audi | a4 | 2.0 | 2008 | 4 | manual(m6) | f | 20 | 31 | p | compact |
| audi | a4 | 2.0 | 2008 | 4 | auto(av) | f | 21 | 30 | p | compact |
| audi | a4 | 2.8 | 1999 | 6 | auto(l5) | f | 16 | 26 | p | compact |
| audi | a4 | 2.8 | 1999 | 6 | manual(m5) | f | 18 | 26 | p | compact |
Mapping defines how the variables are applied to the graphic. So if we were graphing information from mpg, we might map a car’s engine displacement to the \(x\) position and highway mileage to the \(y\) position.
mpg %>%
select(displ, hwy) %>%
rename(x = displ,
y = hwy)## # A tibble: 234 x 2
## x y
## <dbl> <int>
## 1 1.80 29
## 2 1.80 29
## 3 2.00 31
## 4 2.00 30
## 5 2.80 26
## 6 2.80 26
## 7 3.10 27
## 8 1.80 26
## 9 1.80 25
## 10 2.00 28
## # ... with 224 more rows
In ggplot2, data and mapping can be defined either in the initial call to create a graphic:
ggplot(data = mpg, mapping = aes(x = displ, y = hwy))Or when you create a geometric object using a geom_() function:
ggplot() +
geom_(data = mpg, mapping = aes(x = displ, y = hwy))A statistical transformation (stat) transforms the data, generally by summarizing the information. For instance, in a bar graph you typically are not trying to graph the raw data because this doesn’t make any inherent sense. Instead, you might summarize the data by graphing the total number of observations within a set of categories. Or if you have a dataset with many observations, you might transform the data into a smoothing line which summarizes the overall pattern of the relationship between variables.
A stat takes a dataset as input and returns a dataset as output, and so a stat can add new variables to the original dataset. So instead of graphing this data in its raw form:
mpg %>%
select(cyl)## # A tibble: 234 x 1
## cyl
## <int>
## 1 4
## 2 4
## 3 4
## 4 4
## 5 6
## 6 6
## 7 6
## 8 4
## 9 4
## 10 4
## # ... with 224 more rows
You would transform it to:
mpg %>%
count(cyl)## # A tibble: 4 x 2
## cyl n
## <int> <int>
## 1 4 81
## 2 5 4
## 3 6 79
## 4 8 70
Sometimes you don’t need to make a statistical transformation. For example, in a scatterplot you use the raw values for the \(x\) and \(y\) variables to map onto the graph. In these situations, the statistical transformation is an identity transformation - the stat simply passes in the original dataset and exports the exact same dataset.
Statistical transformation functions are called using the stat_() syntax. Used outside of a ggplot object, their output is rather meaningless.
stat_output <- stat_count(data = mpg, mapping = aes(x = cyl))
class(stat_output)## [1] "LayerInstance" "Layer" "ggproto"
stat_output## mapping: x = cyl
## geom_bar: na.rm = FALSE, width = NULL
## stat_count: na.rm = FALSE, width = NULL
## position_stack
Geometric objects (geoms) control the type of plot you create. Geoms are classified by their dimensionality:
A geom is the
ggplot2name for a “mark”.
Each geom can only display certain aesthetics. For example, a point geom has position, color, shape, and size aesthetics.
ggplot(mpg, aes(displ, hwy, color = class)) +
geom_point() +
ggtitle("A point geom with position and color aesthetics")In this example, the aesthetics for shape and size are not used, so instead they take on a constant value for all observations.
A bar geom has position, height, width, and fill.
ggplot(mpg, aes(cyl)) +
geom_bar() +
ggtitle("A bar geom with position and height aesthetics")Again, with this example the fill aesthetic is not used.
Geometric objects are created using geom_() functions. The type of variable(s) you seek to map will dictate which kinds of geom_() functions you can use. For instance, you cannot create a geom_histogram() if your variable is disrete. Instead, you need to use geom_bar().
Sometimes with dense data we need to adjust the position of elements on the plot, otherwise data points might obscure one another. Bar plots frequently stack or dodge the bars to avoid overlap:
count(mpg, class, cyl) %>%
ggplot(aes(cyl, n, fill = class)) +
geom_col(position = "identity") +
ggtitle("An identity bar chart")count(mpg, class, cyl) %>%
ggplot(aes(cyl, n, fill = class)) +
geom_col() +
ggtitle("A stacked bar chart")count(mpg, class, cyl) %>%
ggplot(aes(cyl, n, fill = class)) +
geom_col(position = "dodge") +
ggtitle("A dodged bar chart")Sometimes scatterplots with few unique \(x\) and \(y\) values are jittered (random noise is added) to reduce overplotting.
ggplot(mpg, aes(cyl, hwy)) +
geom_point() +
ggtitle("A point geom with obscured data points")ggplot(mpg, aes(cyl, hwy)) +
geom_jitter() +
ggtitle("A point geom with jittered data points")Position adjustments are defined as functions in R (e.g. position_()), however the normal syntax is to specify an adjustment as an argument to the geom_() function, like:
geom_bar(position = "fill")
geom_bar(position = position_fill())Typically you only directly call the function if you want to specify non-default arguments to that position function.
A scale controls how data is mapped to aesthetic attributes, so we need one scale for every aesthetic property employed in a layer. For example, this graph defines a scale for color:
ggplot(mpg, aes(displ, hwy, color = class)) +
geom_point()Note that the scale is consistent - every point for a compact car is drawn in tan, whereas SUVs are drawn in pink. The scale can be changed to use a different color palette:
ggplot(mpg, aes(displ, hwy, color = class)) +
geom_point() +
scale_color_brewer(palette = "Dark2")Now we are using a different palette, but the scale is still consistent: all compact cars utilize the same color, whereas SUVs use a different color but each SUV uses the same color.
A scale is the
ggplot2name for a “channel”.
Scales are found in the scale_() family of functions.
A coordinate system (coord) maps the position of objects onto the plane of the plot, and control how the axes and grid lines are drawn. Plots typically use two coordinates (\(x, y\)), but could use any number of coordinates. Most plots are drawn using the Cartesian coordinate system:
x1 <- c(1, 10)
y1 <- c(1, 5)
p <- qplot(x1, y1, geom = "blank", xlab = NULL, ylab = NULL) +
theme_bw()
p This system requires a fixed and equal spacing between values on the axes. That is, the graph draws the same distance between 1 and 2 as it does between 5 and 6. The graph could be drawn using a semi-log coordinate system which logarithmically compresses the distance on an axis:
p + coord_trans(y = "log10")Or could even be drawn using polar coordinates:
p + coord_polar()When drawing maps in ggplot2, you will need to employ a map projection coordinate system:
library(fiftystater)
data("fifty_states")
ggplot(data = fifty_states, mapping = aes(x = long, y = lat, group = group)) +
geom_polygon(color = "black", fill = "gray") +
ggtitle("A cartesian coordinate system")ggplot(data = fifty_states, mapping = aes(x = long, y = lat, group = group)) +
geom_polygon(color = "black", fill = "gray") +
coord_map(projection = "albers", lat0 = 25, lat1 = 50) +
ggtitle("Albers equal-area projection")Faceting can be used to split the data up into subsets of the entire dataset. This is a powerful tool when investigating whether patterns are the same or different across conditions, and allows the subsets to be visualized on the same plot (known as conditioned or trellis plots). The faceting specification describes which variables should be used to split up the data, and how they should be arranged.
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
facet_wrap(~ class)ggplot2 contains two faceting functions:
facet_grid() - form a matrix of panels defined by row and column faceting variables. Works best with two discrete variables, and all combinations of the variables exist in the data.facet_wrap() - wraps a one dimensional sequence of panels into two dimensions. Automatically adjusts the number of panels per row to maximize the use of horizontal and vertical space.Why use facet_grid() instead of facet_wrap()?4 facet_grid(x ~ y) will display \(x \times y\) plots even if some plots are empty. For example:
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
facet_grid(cyl ~ class)There are 4 distinct cyl and 7 distinct class values. This plot displays \(4 \times 7 = 28\) plots, even if some are empty (because some classes do not have corresponding cylinder values, like rows with class = "midsize" doesn’t have any corresponding cyl = 5 value ).
facet_wrap(x ~ y) displays only the plots having actual values.
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
facet_wrap(~ cyl + class)There are 19 plots displayed now, one for every combination of cyl and class.
Themes control the display of all non-data elements of the plot. To modify individual elements, use theme(). To select between different premade themes, use the appropriate theme_() function.
sample_plot <- ggplot(mpg, aes(displ, hwy, color = class)) +
geom_point() +
scale_color_brewer(palette = "Dark2")
sample_plot +
theme_gray() +
ggtitle("Standard gray theme")sample_plot +
theme_bw() +
ggtitle("Black and white theme")sample_plot +
theme_light() +
ggtitle("Light theme")sample_plot +
theme_dark() +
ggtitle("Dark theme")sample_plot +
theme_minimal() +
ggtitle("Minimal theme")sample_plot +
theme_void() +
ggtitle("Void theme")sample_plot +
ggthemes::theme_excel() +
labs(title = "Excel theme",
caption = "From the ggthemes package")Rather than explicitly declaring each component of a layered graphic (which will use more code and introduces opportunities for errors), we can establish intelligent defaults for specific geoms and scales. For instance, whenever we want to use a bar geom, we can default to using a stat that counts the number of observations in each group of our variable in the \(x\) position.
ggplot() +
layer(
data = mpg, mapping = aes(x = displ, y = hwy),
geom = "point", stat = "identity", position = "identity"
) +
scale_x_continuous() +
scale_y_continuous() +
coord_cartesian()The above code:
ggplot)layer)
mpg)mapping)geom = "point")stat = "identity" and position = "identity")scale_x_continuous and scale_y_continuous)coord_cartesian)How can we simplify this using intelligent defaults?
Using these defaults, we can rewrite the above code as:
ggplot() +
geom_point(data = mpg, mapping = aes(x = displ, y = hwy))Because multiple layers can use the same components (data, mapping, etc.), we can specify that information in the ggplot function rather than in the layer function:
ggplot(data = mpg, mapping = aes(x = displ, y = hwy)) +
geom_point()And function arguments in R use specific ordering, so we can omit the explicit call to data and mapping:
ggplot(mpg, aes(displ, hwy)) +
geom_point()With this specification, it is easy to build the graphic up with additional layers, without modifying the original code:
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
geom_smooth()## `geom_smooth()` using method = 'loess'
Because we called aes(x = displ, y = hwy) within the ggplot function, it is automatically passed along to both geom_point() and geom_smooth(). If we fail to do this, we get an error:
ggplot(mpg) +
geom_point(aes(displ, hwy)) +
geom_smooth()## `geom_smooth()` using method = 'loess'
## Error: stat_smooth requires the following missing aesthetics: x, y
The graphic is notable for its representation in two dimensions of six types of data: the number of Napoleon’s troops; distance; temperature; the latitude and longitude; direction of travel; and location relative to specific dates.5
# get data on troop movements, city names, and temperatures
troops <- read_table("data/minard-troops.txt")## Parsed with column specification:
## cols(
## long = col_double(),
## lat = col_double(),
## survivors = col_integer(),
## direction = col_character(),
## group = col_integer()
## )
cities <- read_table("data/minard-cities.txt")## Parsed with column specification:
## cols(
## long = col_double(),
## lat = col_double(),
## city = col_character()
## )
temps <- read_table("data/minard-temps.txt") %>%
mutate(date = dmy(date))## Parsed with column specification:
## cols(
## long = col_double(),
## temp = col_integer(),
## month = col_character(),
## day = col_integer(),
## date = col_character()
## )
glimpse(troops)## Observations: 51
## Variables: 5
## $ long <dbl> 24.0, 24.5, 25.5, 26.0, 27.0, 28.0, 28.5, 29.0, 30.0...
## $ lat <dbl> 54.9, 55.0, 54.5, 54.7, 54.8, 54.9, 55.0, 55.1, 55.2...
## $ survivors <int> 340000, 340000, 340000, 320000, 300000, 280000, 2400...
## $ direction <chr> "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A...
## $ group <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...
glimpse(cities)## Observations: 20
## Variables: 3
## $ long <dbl> 24.0, 25.3, 26.4, 26.8, 27.7, 27.6, 28.5, 28.7, 29.2, 30....
## $ lat <dbl> 55.0, 54.7, 54.4, 54.3, 55.2, 53.9, 54.3, 55.5, 54.4, 55....
## $ city <chr> "Kowno", "Wilna", "Smorgoni", "Moiodexno", "Gloubokoe", "...
glimpse(temps)## Observations: 9
## Variables: 5
## $ long <dbl> 37.6, 36.0, 33.2, 32.0, 29.2, 28.5, 27.2, 26.7, 25.3
## $ temp <int> 0, 0, -9, -21, -11, -20, -24, -30, -26
## $ month <chr> "Oct", "Oct", "Nov", "Nov", "Nov", "Nov", "Dec", "Dec", ...
## $ day <int> 18, 24, 9, 14, 24, 28, 1, 6, 7
## $ date <date> 1812-10-18, 1812-10-24, 1812-11-09, 1812-11-14, 1812-11...
Exercise: Write out what the grammar of graphics will look for this graph, just focusing on the map portion.
troopslat and long)survivorsdirectionidentitypathcitieslat and long)cityidentitytextpathFirst we want to build the layer for the troop movement.6 If we just include the troop movement information (ignoring surviviors and direction), we could write this:
ggplot(data = troops,
mapping = aes(x = long, y = lat, group = group)) +
geom_path()We can now add the survivors and direction aesthetics:
ggplot(data = troops,
mapping = aes(x = long, y = lat, group = group,
color = direction, size = survivors)) +
geom_path()The individual segments of the path don’t fit together very well and leave big gaps. We can fix that by adding a rounded line ending to each segment.
ggplot(data = troops,
mapping = aes(x = long, y = lat, group = group,
color = direction, size = survivors)) +
geom_path(lineend = "round")This is not adding a new channel, it is simply adjusting the appearance of the existing channel and how the lines visually appear at the end of each segment.
The size of the path hides the drama of the plot. Napoleon started the 1812 campaign with 422,000 troops and returned with only 10,000. scale_size() automatically uses a default range of c(0,6). We can adjust the scale to allow for more variation in size, highlighting the devastation of the army:
ggplot(data = troops,
mapping = aes(x = long, y = lat, group = group,
color = direction, size = survivors)) +
geom_path(lineend = "round") +
scale_size(range = c(0.5, 15))Finally, we can remove the labels, legends, and change the colors to match the shade of brown from Minard’s original plot.
ggplot(data = troops,
mapping = aes(x = long, y = lat, group = group,
color = direction, size = survivors)) +
geom_path(lineend = "round") +
scale_size(range = c(0.5, 15)) +
scale_color_manual(values = c("#DFC17E", "#252523")) +
labs(x = NULL,
y = NULL) +
guides(color = FALSE,
size = FALSE)Because this graph is actually a map (with vertical and horizontal spatial position corresponding to latitude and longitude), we can easily overlay geographic details like city names and locations. We can locate the city location using geom_point() and label them using geom_text().
Because we are adding a second layer to the graph with a different data source, we can also move the data and mapping arguments to specific geom_() functions
ggplot() +
geom_path(data = troops,
mapping = aes(x = long, y = lat, group = group,
color = direction, size = survivors),
lineend = "round") +
geom_point(data = cities, aes(x = long, y = lat)) +
geom_text(data = cities, aes(x = long, y = lat, label = city)) +
scale_size(range = c(0.5, 15)) +
scale_color_manual(values = c("#DFC17E", "#252523")) +
labs(x = NULL,
y = NULL) +
guides(color = FALSE,
size = FALSE)Doing this places the city names directly at the defined geographical location. This is a bit problematic and makes the graph harder to read. We can adjust the city names using the vjust argument to geom_text():
ggplot() +
geom_path(data = troops,
mapping = aes(x = long, y = lat, group = group,
color = direction, size = survivors),
lineend = "round") +
geom_point(data = cities, aes(x = long, y = lat)) +
geom_text(data = cities, aes(x = long, y = lat, label = city),
vjust = 1.5) +
scale_size(range = c(0.5, 15)) +
scale_color_manual(values = c("#DFC17E", "#252523")) +
labs(x = NULL,
y = NULL) +
guides(color = FALSE,
size = FALSE)Not perfect, but better. We could also adjust the color and font of the text labels to make them more readable:
ggplot() +
geom_path(data = troops,
mapping = aes(x = long, y = lat, group = group,
color = direction, size = survivors),
lineend = "round") +
geom_point(data = cities, aes(x = long, y = lat),
color = "#DC5B44") +
geom_text(data = cities, aes(x = long, y = lat, label = city),
vjust = 1.5,
color = "#DC5B44", family = "sans") +
scale_size(range = c(0.5, 15)) +
scale_color_manual(values = c("#DFC17E", "#252523")) +
labs(x = NULL,
y = NULL) +
guides(color = FALSE,
size = FALSE)To finish this component, let’s remove all the background noise (e.g. grid, gridlines, axis tick marks, axis tick labels). A quick solution is to use the theme_void() function:
troops_cities <- ggplot() +
geom_path(data = troops,
mapping = aes(x = long, y = lat, group = group,
color = direction, size = survivors),
lineend = "round") +
geom_point(data = cities, aes(x = long, y = lat),
color = "#DC5B44") +
geom_text(data = cities, aes(x = long, y = lat, label = city),
vjust = 1.5,
color = "#DC5B44", family = "sans") +
scale_size(range = c(0.5, 15)) +
scale_color_manual(values = c("#DFC17E", "#252523")) +
labs(x = NULL,
y = NULL) +
guides(color = FALSE,
size = FALSE) +
theme_void()
troops_citiesSo far we have four of the variables from Minard’s original plot – we’re still missing the temperatures during the retreat and the days of the retreat. Minard put this information in a separate plot under the map, which is fairly easy to do with gridExtra.
First we have to create the panel, which is a basic line graph with longitude along the x-axis and temperature along the y-axis, with text added at each point.
ggplot(data = temps, aes(x = long, y = temp)) +
geom_line() +
geom_label(aes(label = temp), vjust = 1.5)We can create a new variable for nicer labels, combining temperature with the date.
temps <- temps %>%
mutate(nice_label = str_c(temp, "°, ", month, ". ", day))
ggplot(data = temps, aes(x = long, y = temp)) +
geom_line() +
geom_label(aes(label = nice_label), vjust = 1.5)We’ll also clean up the theme, move the axis label to the right, and only include major horizontal gridlines.
ggplot(data = temps, aes(x = long, y = temp)) +
geom_line() +
geom_label(aes(label = nice_label),
family = "sans", size = 2.5) +
labs(x = NULL,
y = "° Celsius") +
scale_y_continuous(position = "right") +
coord_cartesian(ylim = c(-35, 5)) + # Add some space above/below
theme_bw(base_family = "sans") +
theme(panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
panel.grid.minor.y = element_blank(),
axis.text.x = element_blank(), axis.ticks = element_blank(),
panel.border = element_blank())When we overlay the two plots, we have to make sure the x-axes align, so we need to use the same x-axis limits used in troops_cities. Those limits are buried inside the plot object, the parts of which can be accessed with ggplot_build():
xrange <- ggplot_build(troops_cities)$layout$panel_ranges[[1]]$x.range
xrange## [1] 23.3 38.4
temps_plot <- ggplot(data = temps, aes(x = long, y = temp)) +
geom_line() +
geom_label(aes(label = nice_label),
family = "sans", size = 2.5) +
labs(x = NULL,
y = "° Celsius") +
scale_x_continuous(limits = xrange) +
scale_y_continuous(position = "right") +
coord_cartesian(ylim = c(-35, 5)) + # Add some space above/below
theme_bw(base_family = "sans") +
theme(panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(),
panel.grid.minor.y = element_blank(),
axis.text.x = element_blank(), axis.ticks = element_blank(),
panel.border = element_blank())
temps_plotFinally, we use functions in the gridExtra package to combine the two plots. The easiest way to combine plot objects with gridExtra is to use grid.arrange(), but doing so doesn’t align the axes of the plot. For instance, look at these two example plots — they’re no longer comparable vertically because the left side of the bottom plot extends to the edge of the plot, expanding under the long axis label in the top plot:
example.data <- data_frame(
x = 1:10,
y = rnorm(10)
)
plot1 <- ggplot(example.data, aes(x = x, y = y)) +
geom_line() +
labs(y = "This is a really\nreally really really\nreally tall label")
plot2 <- ggplot(example.data, aes(x = x, y = y)) +
geom_line() +
labs(y = NULL)
grid.arrange(plot1, plot2)Insetad of using grid.arrange, we can use gridExtra’s special version of rbind() (or cbind()) for ggplotGrob objects. A grid graphical object (grob) is a description of a graphical item. These basic classes provide default behavior for validating, drawing, and modifying graphical objects. These are used by grid and gridExtra to redraw graphical objects. ggplotGrob() converts a ggplot object into a grob suitable for use with gridExtra.
plot.both <- gtable_rbind(ggplotGrob(plot1),
ggplotGrob(plot2))
grid::grid.newpage()
grid::grid.draw(plot.both)Now that we can align plots correctly, we can combine the map and the temperature:
both_plot <- gtable_rbind(ggplotGrob(troops_cities),
ggplotGrob(temps_plot))
grid::grid.newpage()
grid::grid.draw(both_plot)They’re aligned, but there’s an obvious problem—the map is way too small and the temperatures are too tall. With grid.arrange it’s possible to pass a vector of relative panel heights, which would let us shrink the bottom panel. While using gtable_rbind() does let us align the two plots, it doesn’t provide an easy way to mess with panel heights. Following this StackOverflow answer, though, we can mess with the ggplot object and adjust the panels manually.
# Identify which layout elements are panels
panels <- both_plot$layout$t[grep("panel", both_plot$layout$name)]
panels## [1] 6 16
# Let's try a 3:1 ratio
both_plot$heights[panels] <- unit(c(3, 1), "null")
grid::grid.newpage()
grid::grid.draw(both_plot)devtools::session_info()## Session info -------------------------------------------------------------
## setting value
## version R version 3.4.3 (2017-11-30)
## system x86_64, darwin15.6.0
## ui X11
## language (EN)
## collate en_US.UTF-8
## tz America/Chicago
## date 2018-04-10
## Packages -----------------------------------------------------------------
## package * version date source
## assertthat 0.2.0 2017-04-11 CRAN (R 3.4.0)
## backports 1.1.2 2017-12-13 CRAN (R 3.4.3)
## base * 3.4.3 2017-12-07 local
## bindr 0.1.1 2018-03-13 CRAN (R 3.4.3)
## bindrcpp 0.2.2.9000 2018-04-08 Github (krlmlr/bindrcpp@bd5ae73)
## broom * 0.4.4 2018-03-29 CRAN (R 3.4.3)
## cellranger 1.1.0 2016-07-27 CRAN (R 3.4.0)
## cli 1.0.0 2017-11-05 CRAN (R 3.4.2)
## colorspace 1.3-2 2016-12-14 CRAN (R 3.4.0)
## compiler 3.4.3 2017-12-07 local
## crayon 1.3.4 2017-10-03 Github (gaborcsardi/crayon@b5221ab)
## datasets * 3.4.3 2017-12-07 local
## devtools 1.13.5 2018-02-18 CRAN (R 3.4.3)
## digest 0.6.15 2018-01-28 CRAN (R 3.4.3)
## dplyr * 0.7.4.9003 2018-04-08 Github (tidyverse/dplyr@b7aaa95)
## evaluate 0.10.1 2017-06-24 CRAN (R 3.4.1)
## forcats * 0.3.0 2018-02-19 CRAN (R 3.4.3)
## foreign 0.8-69 2017-06-22 CRAN (R 3.4.3)
## geosphere 1.5-7 2017-11-05 CRAN (R 3.4.2)
## ggmap * 2.6.1 2016-01-23 CRAN (R 3.4.0)
## ggplot2 * 2.2.1.9000 2018-04-10 Github (tidyverse/ggplot2@3c9c504)
## ggrepel * 0.7.0 2017-09-29 CRAN (R 3.4.2)
## glue 1.2.0 2017-10-29 CRAN (R 3.4.2)
## graphics * 3.4.3 2017-12-07 local
## grDevices * 3.4.3 2017-12-07 local
## grid 3.4.3 2017-12-07 local
## gridExtra * 2.3 2017-09-09 CRAN (R 3.4.1)
## gtable 0.2.0 2016-02-26 CRAN (R 3.4.0)
## haven 1.1.1 2018-01-18 CRAN (R 3.4.3)
## hms 0.4.2 2018-03-10 CRAN (R 3.4.3)
## htmltools 0.3.6 2017-04-28 CRAN (R 3.4.0)
## httr 1.3.1 2017-08-20 CRAN (R 3.4.1)
## jpeg 0.1-8 2014-01-23 CRAN (R 3.4.0)
## jsonlite 1.5 2017-06-01 CRAN (R 3.4.0)
## knitr * 1.20 2018-02-20 CRAN (R 3.4.3)
## lattice 0.20-35 2017-03-25 CRAN (R 3.4.3)
## lazyeval 0.2.1 2017-10-29 CRAN (R 3.4.2)
## lubridate * 1.7.3 2018-02-27 CRAN (R 3.4.3)
## magrittr 1.5 2014-11-22 CRAN (R 3.4.0)
## mapproj 1.2-5 2017-06-08 CRAN (R 3.4.0)
## maps 3.2.0 2017-06-08 CRAN (R 3.4.0)
## memoise 1.1.0 2017-04-21 CRAN (R 3.4.0)
## methods * 3.4.3 2017-12-07 local
## mnormt 1.5-5 2016-10-15 CRAN (R 3.4.0)
## modelr 0.1.1 2017-08-10 local
## munsell 0.4.3 2016-02-13 CRAN (R 3.4.0)
## nlme 3.1-131.1 2018-02-16 CRAN (R 3.4.3)
## pander * 0.6.1 2017-08-06 CRAN (R 3.4.1)
## parallel 3.4.3 2017-12-07 local
## pillar 1.2.1 2018-02-27 CRAN (R 3.4.3)
## pkgconfig 2.0.1 2017-03-21 CRAN (R 3.4.0)
## plyr 1.8.4 2016-06-08 CRAN (R 3.4.0)
## png 0.1-7 2013-12-03 CRAN (R 3.4.0)
## proto 1.0.0 2016-10-29 CRAN (R 3.4.0)
## psych 1.7.8 2017-09-09 CRAN (R 3.4.1)
## purrr * 0.2.4 2017-10-18 CRAN (R 3.4.2)
## R6 2.2.2 2017-06-17 CRAN (R 3.4.0)
## Rcpp 0.12.16 2018-03-13 CRAN (R 3.4.4)
## readr * 1.1.1 2017-05-16 CRAN (R 3.4.0)
## readxl 1.0.0 2017-04-18 CRAN (R 3.4.0)
## reshape2 1.4.3 2017-12-11 CRAN (R 3.4.3)
## RgoogleMaps 1.4.1 2016-09-18 CRAN (R 3.4.0)
## rjson 0.2.15 2014-11-03 CRAN (R 3.4.0)
## rlang 0.2.0.9001 2018-04-10 Github (r-lib/rlang@70d2d40)
## rmarkdown 1.9 2018-03-01 CRAN (R 3.4.3)
## rprojroot 1.3-2 2018-01-03 CRAN (R 3.4.3)
## rstudioapi 0.7 2017-09-07 CRAN (R 3.4.1)
## rvest 0.3.2 2016-06-17 CRAN (R 3.4.0)
## scales 0.5.0.9000 2018-04-10 Github (hadley/scales@d767915)
## sp 1.2-7 2018-01-19 CRAN (R 3.4.3)
## stats * 3.4.3 2017-12-07 local
## stringi 1.1.7 2018-03-12 CRAN (R 3.4.3)
## stringr * 1.3.0 2018-02-19 CRAN (R 3.4.3)
## tibble * 1.4.2 2018-01-22 CRAN (R 3.4.3)
## tidyr * 0.8.0 2018-01-29 CRAN (R 3.4.3)
## tidyselect 0.2.4 2018-02-26 CRAN (R 3.4.3)
## tidyverse * 1.2.1 2017-11-14 CRAN (R 3.4.2)
## tools 3.4.3 2017-12-07 local
## utils * 3.4.3 2017-12-07 local
## withr 2.1.2 2018-04-10 Github (jimhester/withr@79d7b0d)
## xml2 1.2.0 2018-01-24 CRAN (R 3.4.3)
## yaml 2.1.18 2018-03-08 CRAN (R 3.4.4)
Wickham, Hadley. (2010) “A Layered Grammar of Graphics”. Journal of Computational and Graphical Statistics, 19(1).↩
Wilkinson, Leland. (2005). The Grammar of Graphics. (UChicago authentication required)↩
Example drawn from this StackOverflow thread.↩
This exercise is drawn from Wickham, Hadley. (2010) “A Layered Grammar of Graphics”. Journal of Computational and Graphical Statistics, 19(1). and Andrew Heiss’s extension using ggplot2↩
Example drawn from Exploring Minard’s 1812 plot with ggplot2.↩